﻿using System;
using System.IO;
using System.ServiceProcess;
using System.Windows.Forms;
using MediaPortal.DeployTool;
using Microsoft.HomeServer.Controls;

namespace Microsoft.HomeServer.HomeServerConsoleTab.TV4Home.Server.WHS.AddIn.Wizards.InstallTVServer
{
    #region Wizard
    public class InstallTVServerWizard : Wizard
    {
        /// <summary>
        /// Initializes a new instance of the InstallTVServerWizard class.
        /// </summary>
        public InstallTVServerWizard()
        {
            base.Width = CommonSettings.BigWizardStdWidth;
            base.Height = CommonSettings.BigWizardStdHeight;
            base.WizardIcon = Properties.Resources.SetupTv;
            base.Text = Resources_InstallTVServerWizard.InstallTVServerWizard_WizardTitle;
            base.Init();

            // set the available wizard pages
            base.SetTypes(new Type[]
                              {
                                  typeof (WPWelcome),
                                  typeof (WPInfo),
                                  typeof (WPDatabaseType),
                                  typeof (WPDatabaseSettings),
                                  typeof (WPTvServerSettings),
                                  typeof (WPInstallation),
                                  typeof (WPFinished)
                              });

            base.DialogResult = DialogResult.Cancel;
            InstallationProperties.Instance.Clear();
        }
    }
    #endregion

    #region WizardPages
    class WPWelcome : WizardPage
    {
        WPWelcomeControl pageControl;

        public WPWelcome(Wizard parentWizard, object currentSubject, object currentParameter1)
        {
            if (parentWizard == null)
                throw new ArgumentNullException();

            base.ParentWizard = parentWizard;

            base.OkButtonName = CommonSettings.WizardStandardButtonNameNext;
            base.CancelButtonName = CommonSettings.WizardStandardButtonNameCancel;
            base.BackButtonName = CommonSettings.WizardStandardButtonNameBack;

            base.HideLeftBanner = false;
            base.HideTopBannerAndLines = true;

            base.Init();

            base.OkButtonhook(this.OnNext);
            base.OkButtonEnabled = true;
            base.BackButtonEnabled = false;

            pageControl = new WPWelcomeControl();

            base.PlaceControlIntoWizardPage(pageControl);
        }

        public override void StartRealWork()
        {
        }

        private void OnNext(object obj, EventArgs ea)
        {
            InstallationProperties.Instance.Set("InstallType", "tvserver_master");

            base.ParentWizard.SetCurrentPanel(typeof(WPInfo));
        }
    }

    class WPInfo : WizardPage
    {
        WPInfoControl pageControl;

        public WPInfo(Wizard parentWizard, object currentSubject, object currentParameter1)
        {
            if (parentWizard == null)
                throw new ArgumentNullException();

            base.ParentWizard = parentWizard;

            base.TitleText = Resources_InstallTVServerWizard.WPInfo_WizardPage_TitleText;

            base.OkButtonName = CommonSettings.WizardStandardButtonNameNext;
            base.CancelButtonName = CommonSettings.WizardStandardButtonNameCancel;
            base.BackButtonName = CommonSettings.WizardStandardButtonNameBack;

            base.HideTopBannerAndLines = false;

            base.Init();

            base.OkButtonhook(this.OnNext);
            base.BackButtonhook(this.OnBack);

            base.OkButtonEnabled = true;
            base.BackButtonEnabled = true;

            pageControl = new WPInfoControl();

            base.PlaceControlIntoWizardPage(pageControl);
        }

        public override void StartRealWork()
        {
        }

        private void OnNext(object obj, EventArgs ea)
        {
            // check internet connection and proceed if available
            if (MediaPortal.DeployTool.InstallationChecks.InternetChecker.CheckConnection())
            {
                base.ParentWizard.SetCurrentPanel(typeof(WPDatabaseType));
            }
            else
            {
                QMessageBox.Show("Couldn't connect to the Internet!", "Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
            }
        }

        private void OnBack(object obj, EventArgs ea)
        {
            base.ParentWizard.SetCurrentPanel(typeof(WPWelcome));
        }
    }

    class WPDatabaseType : WizardPage
    {
        WPDatabaseTypeControl pageControl;

        public WPDatabaseType(Wizard parentWizard, object currentSubject, object currentParameter1)
        {
            if (parentWizard == null)
                throw new ArgumentNullException();

            base.ParentWizard = parentWizard;

            base.TitleText = Resources_InstallTVServerWizard.WPDatabaseType_WizardPage_TitleText;

            base.OkButtonName = CommonSettings.WizardStandardButtonNameNext;
            base.CancelButtonName = CommonSettings.WizardStandardButtonNameCancel;
            base.BackButtonName = CommonSettings.WizardStandardButtonNameBack;

            base.HideTopBannerAndLines = false;

            base.Init();

            base.OkButtonhook(this.OnNext);
            base.BackButtonhook(this.OnBack);

            base.OkButtonEnabled = true;
            base.BackButtonEnabled = true;

            pageControl = new WPDatabaseTypeControl();

            base.PlaceControlIntoWizardPage(pageControl);
        }

        public override void StartRealWork()
        {
        }

        private void OnNext(object obj, EventArgs ea)
        {
            // remove DBMSDir key if it exists, so that the next page can set the correct directory (e.g. when the user has changed the DB selection)
            InstallationProperties.Instance.Remove("DBMSDir");

            switch (pageControl.SelectedDatabaseType)
            {
                case WPDatabaseTypeControl.DatabaseType.MSSQL:
                    InstallationProperties.Instance.Set("DBMSType", "msSQL2005");
                    break;
                case WPDatabaseTypeControl.DatabaseType.MySQL:
                    InstallationProperties.Instance.Set("DBMSType", "mysql");
                    break;
                case WPDatabaseTypeControl.DatabaseType.SQLAlreadyPresent:
                    InstallationProperties.Instance.Set("DBMSType", "DBAlreadyInstalled");
                    break;
            }

            // if DB server is already installed, go directly to the TV server settings page; otherwise go to the DB settings page
            if (pageControl.SelectedDatabaseType == WPDatabaseTypeControl.DatabaseType.SQLAlreadyPresent)
                base.ParentWizard.SetCurrentPanel(typeof(WPTvServerSettings));
            else
                base.ParentWizard.SetCurrentPanel(typeof(WPDatabaseSettings));
        }

        private void OnBack(object obj, EventArgs ea)
        {
            base.ParentWizard.SetCurrentPanel(typeof(WPInfo));
        }
    }

    class WPDatabaseSettings : WizardPage
    {
        WPDatabaseSettingsControl pageControl;

        public WPDatabaseSettings(Wizard parentWizard, object currentSubject, object currentParameter1)
        {
            if (parentWizard == null)
                throw new ArgumentNullException();

            base.ParentWizard = parentWizard;

            base.TitleText = Resources_InstallTVServerWizard.WPDatabaseSettings_WizardPage_TitleText;

            base.OkButtonName = CommonSettings.WizardStandardButtonNameNext;
            base.CancelButtonName = CommonSettings.WizardStandardButtonNameCancel;
            base.BackButtonName = CommonSettings.WizardStandardButtonNameBack;

            base.HideTopBannerAndLines = false;

            base.Init();

            base.OkButtonhook(this.OnNext);
            base.BackButtonhook(this.OnBack);

            base.OkButtonEnabled = true;
            base.BackButtonEnabled = true;

            pageControl = new WPDatabaseSettingsControl();

            base.PlaceControlIntoWizardPage(pageControl);
        }

        public override void StartRealWork()
        {
            pageControl.InitializeUI();
        }

        private void OnNext(object obj, EventArgs ea)
        {
            InstallationProperties.Instance.Set("DBMSDir", pageControl.DBDirectory);
            InstallationProperties.Instance.Set("DBMSPassword", pageControl.DBUserPassword);
            InstallationProperties.Instance.Set("ConfigureDBMSFirewall", pageControl.ConfigureFirewallDB ? "1" : "0");

            base.ParentWizard.SetCurrentPanel(typeof(WPTvServerSettings));
        }

        private void OnBack(object obj, EventArgs ea)
        {
            base.ParentWizard.SetCurrentPanel(typeof(WPDatabaseType));
        }
    }

    class WPTvServerSettings : WizardPage
    {
        WPTvServerSettingsControl pageControl;

        public WPTvServerSettings(Wizard parentWizard, object currentSubject, object currentParameter1)
        {
            if (parentWizard == null)
                throw new ArgumentNullException();

            base.ParentWizard = parentWizard;

            base.TitleText = Resources_InstallTVServerWizard.WPTvServerSettings_WizardPage_TitleText;

            base.OkButtonName = CommonSettings.WizardStandardButtonNameNext;
            base.CancelButtonName = CommonSettings.WizardStandardButtonNameCancel;
            base.BackButtonName = CommonSettings.WizardStandardButtonNameBack;

            base.HideTopBannerAndLines = false;

            base.Init();

            base.OkButtonhook(this.OnNext);
            base.BackButtonhook(this.OnBack);

            base.OkButtonEnabled = true;
            base.BackButtonEnabled = true;

            pageControl = new WPTvServerSettingsControl();

            base.PlaceControlIntoWizardPage(pageControl);
        }

        public override void StartRealWork()
        {
            pageControl.InitializeUI();
        }

        private void OnNext(object obj, EventArgs ea)
        {
            InstallationProperties.Instance.Set("TVServerDir", pageControl.TvServerDirectory);
            InstallationProperties.Instance.Set("ConfigureTVServerFirewall", pageControl.ConfigureFirewallTVServer ? "1" : "0");

            base.ParentWizard.SetCurrentPanel(typeof(WPInstallation));
        }

        private void OnBack(object obj, EventArgs ea)
        {
            if (InstallationProperties.Instance["DBMSType"] == "DBAlreadyInstalled")
                base.ParentWizard.SetCurrentPanel(typeof(WPDatabaseType));
            else
                base.ParentWizard.SetCurrentPanel(typeof(WPDatabaseSettings));
        }
    }

    class WPInstallation : WizardPage
    {
        WPInstallationControl pageControl;

        public WPInstallation(Wizard parentWizard, object currentSubject, object currentParameter1)
        {
            if (parentWizard == null)
                throw new ArgumentNullException();

            base.ParentWizard = parentWizard;

            base.TitleText = Resources_InstallTVServerWizard.WPInstallation_WizardPage_TitleText;

            base.OkButtonName = Resources_InstallTVServerWizard.WPInstallation_WizardPage_NextButtonText;
            base.CancelButtonName = CommonSettings.WizardStandardButtonNameCancel;
            base.BackButtonName = CommonSettings.WizardStandardButtonNameBack;

            base.HideTopBannerAndLines = false;

            base.Init();

            base.OkButtonhook(this.OnNext);
            base.BackButtonhook(this.OnBack);

            base.OkButtonEnabled = true;
            base.BackButtonEnabled = true;

            pageControl = new WPInstallationControl();

            base.PlaceControlIntoWizardPage(pageControl);
        }

        public override void StartRealWork()
        {
            pageControl.PopulateListView();
        }

        private bool firstOnNext = true;
        private bool installSucceeded = false;

        private void OnNext(object obj, EventArgs ea)
        {
            if (firstOnNext)
            {
                // stop the TV4Home.Server.CoreService, so it doesn't interfere with installation
                StartStopTV4HomeCoreService(false);

                base.OkButtonEnabled = false;
                base.BackButtonEnabled = false;
                base.CancelButtonEnabled = false;
                base.ParentWizard.PreventClose = true;

                FileSystemWatcher directoryWatcher = new FileSystemWatcher();
                FileSystemWatcher configFilesWatcher = new FileSystemWatcher() { Filter = "*.exe.config" };

                try
                {
                    // create directory where install files are downloaded
                    Directory.CreateDirectory(Application.StartupPath + @"\deploy");

                    // create the Team MediaPortal base directory
                    Directory.CreateDirectory(Utils.CommonPaths.TeamMediaPortal_InstallDirectory);

                    directoryWatcher.Path = Utils.CommonPaths.TeamMediaPortal_InstallDirectory;

                    // wait for a subdirectory "MediaPortal TV Server" to be created, then start the configFilesWatcher
                    directoryWatcher.Created += (sender, e) =>
                                                    {
                                                        if (e.Name == "MediaPortal TV Server")
                                                        {
                                                            configFilesWatcher.Path = Utils.CommonPaths.TVServer_InstallDirectory;
                                                            configFilesWatcher.EnableRaisingEvents = true;
                                                            directoryWatcher.EnableRaisingEvents = false;
                                                        }
                                                    };

                    // watch for config files and rename them to .config instead of .exe.config (which doesn't work on Win2k3)
                    configFilesWatcher.Created += (sender, e) => new System.Threading.Thread(() => ProcessFile(e.FullPath)) { IsBackground = true }.Start();

                    directoryWatcher.EnableRaisingEvents = true;

                    // perform the installation
                    installSucceeded = pageControl.InstallComponents();
                }
                catch (Exception ex)
                {
                    MessageBox.Show(ex.Message + ex.StackTrace);
                }
                finally
                {
                    // stop watching
                    directoryWatcher.EnableRaisingEvents = false;
                    configFilesWatcher.EnableRaisingEvents = false;

                    // cleanup resources
                    directoryWatcher.Dispose();
                    configFilesWatcher.Dispose();
                }

                base.OkButton.Text = CommonSettings.WizardStandardButtonNameNext;
                base.OkButtonEnabled = true;
                base.ParentWizard.PreventClose = false;

                firstOnNext = false;

                // start the previously stopped TV4Home.Server.CoreService again
                StartStopTV4HomeCoreService(true);
            }
            else
            {
                base.ParentWizard.SetCurrentPanel(typeof(WPFinished), !installSucceeded);
            }
        }

        private static void ProcessFile(string filePath)
        {

            try
            {
                // wait until the file can be accessed
                while (!FileIsAccessible(filePath))
                {
                    System.Threading.Thread.Sleep(50);
                }

                // rename the config file to .config instead of .exe.config (which doesn't work on Win2k3)
                File.Move(filePath, Path.GetDirectoryName(filePath) + "\\" + Path.GetFileName(filePath).Replace(".exe.config", ".config"));
            }
            catch (Exception ex)
            {
                MessageBox.Show(ex.Message + ex.StackTrace);
            }
        }

        private static bool FileIsAccessible(string filePath)
        {
            try
            {
                // check if we can open the file exclusively
                using (FileStream inputStream = File.Open(filePath, FileMode.Open, FileAccess.Read, FileShare.None))
                    return true;
            }
            catch (IOException)
            {
                // file may be in use by another process
                return false;
            }
        }

        private void StartStopTV4HomeCoreService(bool start)
        {
            ServiceController service = new ServiceController("TV4HomeCoreService");

            try
            {
                TimeSpan timeout = TimeSpan.FromSeconds(3);

                if (start)
                {
                    service.Start();
                    service.WaitForStatus(ServiceControllerStatus.Running, timeout);
                }
                else
                {
                    service.Stop();
                    service.WaitForStatus(ServiceControllerStatus.Stopped, timeout);
                }
            }
            catch (Exception ex)
            {
                //TODO: log exception
            }
        }

        private void OnBack(object obj, EventArgs ea)
        {
            base.ParentWizard.SetCurrentPanel(typeof(WPTvServerSettings));
        }
    }

    class WPFinished : WizardPage
    {
        Control pageControl;

        public WPFinished(Wizard parentWizard, object currentSubject, object currentParameter1)
        {
            if (parentWizard == null)
                throw new ArgumentNullException();

            base.ParentWizard = parentWizard;

            base.OkButtonName = CommonSettings.WizardStandardButtonNameNext;
            base.BackButtonName = CommonSettings.WizardStandardButtonNameBack;
            base.CancelButtonName = CommonSettings.WizardStandardButtonNameCancel;

            base.HideLeftBanner = false;
            base.HideTopBannerAndLines = true;

            base.Init();

            base.OkButtonVisible = false;
            base.BackButtonVisible = false;

            base.CancelButtonName = CommonSettings.WizardStandardButtonNameDone;
            base.CancelButton.Text = CommonSettings.WizardStandardButtonNameDone;
            base.CancelButton.DialogResult = DialogResult.OK;

            base.CancelButtonEnabled = true;
            base.CancelButton.Select();

            base.HideTopBannerAndLines = false;

            base.CancelButtonhook(this.OnNext);

            if (base.IsAtError)
                pageControl = new WPFinishedErrorControl();
            else
                pageControl = new WPFinishedControl();

            base.PlaceControlIntoWizardPage(pageControl);
        }

        public override void StartRealWork()
        {
        }

        private void OnNext(object obj, EventArgs ea)
        {
            // wizard comleted
            base.ParentWizard.DialogResult = DialogResult.OK;
            base.Invalidate();
        }
    }
    #endregion
}
